﻿///////////////////////////////////////////////////////////////////////////////
//
// This file is part of the BassDrop project
//   https://code.google.com/p/bass-drop/
//
//
// Creator:	Gergely Krajcsovszki (krajcsovszkig@hotmail.com)
// Date:	2013-09-21 20:30
//
//
// Copyright © 2013-2014. Gergely Krajcsovszki
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace BassDrop.Core.Common.Media.Playlists
{
	/// <summary>
	/// An item that can be put in a playlist. Can be a track
	/// or an action
	/// </summary>
	public abstract class PlaylistItemBase : INotifyPropertyChanged
	{
		#region Types

		/// <summary>
		/// Current state of playback
		/// </summary>
		public enum PlayState
		{
			/// <summary>
			/// The track is currently being initialized
			/// </summary>
			Initializing,
			/// <summary>
			/// Track could be played but is not currently playing
			/// and Position is null
			/// </summary>
			Stopped,
			/// <summary>
			/// Track could be played but is not currently playing
			/// and Position is not null
			/// </summary>
			Paused,
			/// <summary>
			/// Track is currently being played
			/// </summary>
			Playing,
			/// <summary>
			/// Track is currently being played but the stream is blocked
			/// due to seeking, waiting for data or some other reason.
			/// Technically it is Paused, but not on purpose, or only temporarily.
			/// </summary>
			Seeking,
			/// <summary>
			/// Same as Stopped, but this time it stopped because it finished playing
			/// (got to the end of the track)
			/// </summary>
			Finished,
			/// <summary>
			/// Track cannot be played (but is not null)
			/// </summary>
			Error
		}

		#endregion Types

		#region Properties

		/// <summary>
		/// Is this item the currently played one
		/// </summary>
		private bool m_selected;
		/// <summary>
		/// Is this item the currently played one
		/// </summary>
		public bool Selected
		{
			get
			{
				return m_selected;
			}

			set
			{
				if ( m_selected != value )
				{
					m_selected = value;
					OnPropertyChanged( new PropertyChangedEventArgs( "Selected" ) );
				}
			}
		}

		/// <summary>
		/// The unique ID of this item in the media library.<para/>
		/// Normal tracks have an Id starting from 1001, action items have an Id below 1000
		/// specific to their type.<para/>
		/// Tracks that are not in the library have a null ID.
		/// </summary>
		public abstract long? LibraryId
		{
			get;
		}

		/// <summary>
		/// The playlist containing this item
		/// </summary>
		public Playlist Parent
		{
			get;
			set;
		}

		/// <summary>
		/// Gets the length of the item in milliseconds
		/// 
		/// Null if the length is indeterminate
		/// </summary>
		public abstract long? Length
		{
			get;
		}

		/// <summary>
		/// Error occured while handling the item
		/// </summary>
		public abstract Exception Error // TODO: írja ki valahol
		{
			get;
			set;
		}

		/// <summary>
		/// The current state of playback
		/// </summary>
		private PlayState m_currentPlayState = PlayState.Initializing;
		/// <summary>
		/// Gets the current state of playback
		/// </summary>
		public PlayState CurrentPlayState
		{
			get
			{
				return m_currentPlayState;
			}

			set
			{
				if ( m_currentPlayState != value )
				{
					m_currentPlayState = value;

					if ( PlayStateChanged != null )
					{
						PlayStateChanged( this, new PropertyChangedEventArgs( "CurrentPlayState" ) );
					}

					if ( PropertyChanged != null )
					{
						PropertyChanged( this, new PropertyChangedEventArgs( "CurrentPlayState" ) );
					}
				}

				// if the playback of this track is finished or errored, advance the playlist
				if ( Selected && (m_currentPlayState == PlayState.Finished || m_currentPlayState == PlayState.Error) )
				{
					Parent.Next();
				}
			}
		}

		/// <summary>
		/// A mediaplayer that can play this item
		/// 
		/// Null if none found
		/// </summary>
		public abstract MediaDecoderBase MediaPlayer
		{
			get;
		}

		#endregion Properties

		#region Events

		/// <summary>
		/// Event fired when a property of the item or the referenced media track changes
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;

		/// <summary>
		/// Fires a PropertyChanged event
		/// </summary>
		/// <param name="e"></param>
		protected void OnPropertyChanged( PropertyChangedEventArgs e )
		{
			if ( PropertyChanged != null )
			{
				PropertyChanged( this, e );
			}
		}

		/// <summary>
		/// Event for changes to the PlayState property
		/// </summary>
		public event PropertyChangedEventHandler PlayStateChanged;

		#endregion Events

		/// <summary>
		/// Default constructor to create a new item
		/// </summary>
		protected PlaylistItemBase()
		{
		}

		/// <summary>
		/// Starts playing/executing this item
		/// </summary>
		public abstract void Play();

		/// <summary>
		/// Pauses playing/executing this item (remembers position)
		/// </summary>
		public abstract void Pause();

		/// <summary>
		/// Stops playing/executing this item (resets position)
		/// </summary>
		public abstract void Stop();

		/// <summary>
		/// Resets the position of this track to the beginning. Doesn't change play state
		/// </summary>
		public abstract void Reset();

		/// <summary>
		/// Removes this item from its parent
		/// </summary>
		public void RemoveFromParent()
		{
			if ( Parent != null )
			{
				Parent.Remove( this );
			}
		}

		/// <summary>
		/// Creates a copy of this object. The copy can be shallow, deep or mixed.
		/// </summary>
		/// <returns></returns>
		public abstract PlaylistItemBase Clone();
	}
}
